import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

import javax.xml.transform.TransformerException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import ualrcdg.shared.CDGXMLHelper;
import ualrcdg.shared.XMLHelper;
import ualrcdg.shared.exceptions.CDGException;


/**
 * This class provides a way for the client application to communicate with the server using sockets
 */
public class Communicator
{

	private final Socket socket;
	private final DataInputStream dis;
	private final DataOutputStream dos;
	private final static String hostname = "localhost";
	private final static int portnumber = 3333;

	/**
	 * This constructor provides a communicator with a socket.
	 */
	public Communicator( ) throws CDGException
	{
		try
		{
			this.socket = new Socket( Communicator.hostname, Communicator.portnumber );
		}
		catch( IOException e )
		{
			throw new CDGException( "Error in Communicator: unable to connect to server.", e );
		}
		try
		{
			dis = new DataInputStream( socket.getInputStream( ) );
		}
		catch( IOException e )
		{
			throw new CDGException( "Error in Communicator: unable to fetch input stream.", e );
		}
		try
		{
			dos = new DataOutputStream( socket.getOutputStream( ) );
		}
		catch( IOException e )
		{
			throw new CDGException( "Error in Communicator: unable to fetch input stream.", e );
		}

		System.out.println( "New Communicator started with socket = " + socket.toString( ) );
	}

	/**
	 * Sends a DOM request object to the CDG Server (via this object's socket) and returns the response given by the server
	 * @param request DOM request object to be sent to the server
	 * @return response DOM response object received from the server
	 */
	public Document sendRequest( Document request )
	{
		if( !sendFile( request ) )
		{
			closeSocket( );
			return null;
		}
		
		Document response = receiveFile( );
		
		if( response == null )
		{
			closeSocket( );
			return null;
		}

		//LATER check id of response to match with request
		return response;
	}

	/**
	 * Convenience method for receiving a request file from this Communicator's input stream.
	 * 
	 * @return an XML document request from a CDG Client application instance
	 */
	public Document receiveFile( )
	{
		try
		{
			System.out.println( "Communicator: trying to receiveFile" );
			if( socket.isClosed( ) || !socket.isConnected( ) )
			{
				throw new IOException( "Error in Communicator: socket closed before request document could be read." );
			}
			
			int bytesRemaining = dis.readInt( );
			byte[] message = new byte[bytesRemaining];
			dis.read( message );
			
			return XMLHelper.parseDocumentFromBytes( message );
		}
		catch(  SAXException | IOException | TransformerException e )
		{
			System.out.println( "Error in Communicator: unable to parse request document from stream." );
			e.printStackTrace( );
			return null;
		}
	}
	
	/**
	 * Convenience method for sending a file to this Communicator's output stream.
	 * 
	 * @param file the XML response file to send to the CDG Client associated with this Communicator. 
	 * @return true if the file was successfully sent, false otherwise
	 */
	private boolean sendFile( Document file )
	{
		try
		{
			byte[] message = CDGXMLHelper.writeDocumentToBytes( file );
			dos.writeInt( message.length );
			dos.write( message );
			dos.flush( );
			return true;
		}
		catch( IOException |TransformerException e )
		{
			System.out.println( "Error in Communicator: unable to write response document to stream." );
			e.printStackTrace( );
			return false;
		}		
	}
	
	/**
	 * Closes this Communicator's socket and logs any exceptions that occur.  
	 */
	public void closeSocket( )
	{
		try
		{
			socket.shutdownInput( );
			socket.shutdownOutput( );
			socket.close( );
		}
		catch( IOException e )
		{
			System.out.println( "Error in Communicator: unable to close socket ( " + socket.toString( ) + " )" );
		}

	}
}